# -*- coding:utf8 -*-

from django.conf.urls import url
from wanbatvcom.http.decorators import require_params
from wanbatvcom.http.whttp import send_error_response_with_message, \
    send_ok_response_with_data, \
    send_error_response_on_exception, \
    send_response_with_result
from wanbatvcom import utils
from wanbatvcom.settings import PRIVATE_ROOT, CDN_URL, SECRET_KEY, TEMP_ROOT
from wanplus import config

from wanplus.models.common import Application, Subject, AppVersion, AppResVersion, AppResource

import json
import os
import time
import hashlib
import datetime
import urllib2
import urllib
from wanplus.utils import ReleaseStatus

__author__ = 'Forcs'

class CommonService(object):

    def __init__(self):
        pass

    @property
    def urls(self):
        urlpatterns = [
            url(r'^app/version/add$', self.add_app_version, name='common_add_app_version'),
            url(r'^app/version/preview/(?P<ver_token>\w+)', self.version_preview, name='common_version_preview'),
            url(r'^app/version/release/(?P<ver_token>\w+)', self.version_release, name='common_version_release'),
            url(r'^app/register$', self.register_application, name='common_register_application'),
            url(r'^app/apk$', self.add_app_apk, name='common_application_apk'),

            url(r'^subject/register$', self.register_subject, name='common_register_subject'),
            url(r'^subject/preview/(?P<subject_code>\w+)$', self.subject_preview, name='common_subject_preview'),
            url(r'^subject/release/(?P<subject_code>\w+)$', self.subject_release, name='common_subject_release'),

            url(r'^app/resource/save/(?P<version>\w+)$', self.save_app_resource_file, name='common_save_app_res'),
            url(r'^app/resource/time/(?P<res_token>\w+)$', self.save_app_resource_time, name='common_time_app_res'),
            url(r'^app/resource/version/prepublish', self.prepublish_res_version, name='common_prepublish_res_version'),
            url(r'^app/resource/version/preview/(?P<version>\w+)$', self.sync_res_version_preview,
                name='common_res_ver_preview'),
            url(r'^app/resource/version/release/(?P<version>\w+)$', self.sync_res_version_release,
                name='common_res_ver_release'),
        ]
        return urlpatterns

    @require_params(["name", "price", "type", "apptoken", "code"], method="POST")
    def register_subject(self, request):

        body = request.POST

        code = body["code"]
        name = body["name"]
        price = float(body["price"])
        type = body["type"]
        app_token = body["apptoken"]

        try:
            app_model = Application.objects.get(token=app_token)
            now = datetime.datetime.now()

            subject_model = Subject()
            subject_model.code = code
            subject_model.name = name
            subject_model.type = type
            subject_model.date_added = now
            subject_model.price = int(price * 100)
            subject_model.application = app_model
            subject_model.save()

            return send_ok_response_with_data(data={
                "subjectCreate": True,
                "code": code,
                "name": name,
            }, message='成功创建{}的产品包 {}，编号 {}'.format(app_model.name.encode("utf8"), name.encode("utf8"), code))
        except Exception as e:
            return send_error_response_with_message(message='无法创建产品包，应用包不存在')

    def _sync_subject(self, subject_code, host=None, status=ReleaseStatus.NONE):
        if not subject_code:
            return send_error_response_with_message(message='产品包id无效')

        try:
            subject = Subject.objects.get(code=subject_code)
        except Exception as e:
            return send_error_response_on_exception(e)

        if not subject:
            return send_error_response_with_message('无此产品 %s' % subject_code)

        subject_data = {
            "code": subject.code,
            "name": subject.name.encode('utf8'),
            "price": subject.price,
            "type": subject.type,
            "apptoken": subject.application.token
        }
        req = urllib2.Request("http://%s/api/common/sync/subject" % host, data=urllib.urlencode(subject_data))
        res = urllib2.urlopen(req)
        res_content = res.read()
        res.close()
        res_obj = json.loads(res_content, encoding='utf8')
        if res_obj["resultCode"] != 1:
            return send_response_with_result(res_content)

        subject.release_state = status
        subject.save()

        return send_ok_response_with_data(data={
            "syncSubject": True
        })

    def subject_preview(self, request, subject_code=None):
        return self._sync_subject(subject_code, config.PREVIEW_HOST, ReleaseStatus.PREVIEW)

    def subject_release(self, request, subject_code=None):
        return self._sync_subject(subject_code, config.RELEASE_HOST, ReleaseStatus.RELEASE)

    @require_params(["code", "name", "appid", "package", "platform"], method="POST")
    def register_application(self, request):

        body = request.POST
        code = body["code"]
        name = body["name"].encode("utf8")
        app_id = body["appid"]
        package = body["package"]
        platform = body["platform"]

        try:
            # 序列号应用信息
            info = 'c={}&n={}&a={}&pk={}&pf={}'.format(code, name, app_id, package.encode("utf8"), platform)
            # 生成MD5签名
            md5 = hashlib.md5()
            md5.update(info)
            token = md5.hexdigest()
            # 生成签名串，SHA1加密
            sha1 = hashlib.sha1()
            sha1.update('t={}&k={}'.format(token, SECRET_KEY))
            sign_key = sha1.hexdigest()

            app_data = {
                "code": code,
                "name": name,
                "appid": app_id,
                "package": package,
                "platform": platform,
                "token": token,
                "signkey": sign_key
            }

            req = urllib2.Request("http://121.201.7.173:8087/api/common/sync/application",
                                  data=urllib.urlencode(app_data))
            res = urllib2.urlopen(req)
            res_content = res.read()
            res.close()
            res_obj = json.loads(res_content, encoding='utf8')
            if res_obj["resultCode"] != 1:
                return send_response_with_result(res_content)

            req = urllib2.Request("http://121.201.14.248:8062/api/common/sync/application",
                                  data=urllib.urlencode(app_data))
            res = urllib2.urlopen(req)
            res_content = res.read()
            res.close()
            res_obj = json.loads(res_content, encoding='utf8')
            if res_obj["resultCode"] != 1:
                return send_response_with_result(res_content)

            app_model = Application()
            app_model.code = code  # 唯一
            app_model.name = name
            app_model.app_id = app_id  # 唯一
            app_model.package = package
            app_model.platform = platform
            app_model.sign_key = sign_key
            app_model.token = token
            app_model.save()

            return send_ok_response_with_data(data={
                "appId": token,
                "appKey": sign_key
            })
        except Exception as e:
            return send_error_response_on_exception(exception=e)

    @require_params(["package", "versionname", "versioncode", "builddate",
                     "downloadurl", "md5", "flavor"],
                    method='POST')
    def add_app_version(self, request):

        body = request.POST
        package = body["package"]

        try:
            app_model = Application.objects.get(package=package)

            version_name = body["versionname"]
            version_code = body["versioncode"]
            date_build = body["builddate"]
            md = body["md5"]
            now = datetime.datetime.now()

            md5 = hashlib.md5()
            md5.update('p=%s&vn=%s&vc=%s&bd=%s&md=%s' % (package, version_name, version_code, date_build, md))
            token = md5.hexdigest()

            version_model, create = AppVersion.objects.get_or_create(token=token)
            version_model.version_name = version_name
            version_model.version_code = version_code
            version_model.flavor = body["flavor"]
            version_model.date_build = date_build
            version_model.date_added = now
            version_model.md5 = md
            version_model.dl_url = body["downloadurl"]
            version_model.application = app_model
            version_model.save()

            return send_ok_response_with_data(data={
                "saveVersion": True
            })
        except Exception as e:
            return send_error_response_on_exception(e)

    def _sync_version(self, token, host=None, status=ReleaseStatus.NONE):
        if not token:
            return send_error_response_with_message(message='版本标识无效')

        try:
            version_model = AppVersion.objects.get(token=token)
        except Exception as e:
            return send_error_response_on_exception(e)

        if not version_model:
            return send_error_response_with_message(message='无此版本 %s ' % token)

        version_data = {
            "versionname": version_model.version_name,
            "versioncode": version_model.version_code,
            "flavor": version_model.flavor,
            "md5": version_model.md5,
            "token": version_model.token,
            "builddate": version_model.date_build,
            "downloadurl": version_model.dl_url,
            "apptoken": version_model.application.token
        }

        req = urllib2.Request("http://%s/api/common/sync/version" % host, urllib.urlencode(version_data))
        res = urllib2.urlopen(req)
        res_content = res.read()
        res.close()
        res_obj = json.loads(res_content, encoding='utf8')
        if res_obj["resultCode"] != 1:
            return send_response_with_result(res_content)

        version_model.release_status = status
        version_model.save()

        return send_ok_response_with_data(data={
            "syncVersion": True
        })

    def version_preview(self, request, ver_token=None):
        return self._sync_version(ver_token, config.PREVIEW_HOST, ReleaseStatus.PREVIEW)

    def version_release(self, request, ver_token=None):
        return self._sync_version(ver_token, config.RELEASE_HOST, ReleaseStatus.RELEASE)

    def add_app_apk(self, request):
        form_file = request.FILES
        apk_file_obj = form_file["file"]
        apk_file_name = apk_file_obj.name
        suffix = apk_file_name.split(".")[-1]
        timestamp = time.time()
        md5 = hashlib.md5()
        md5.update("{}{}".format(apk_file_name.encode("utf8"), timestamp))
        file_name_encode = md5.hexdigest()
        from wanbatvcom.settings import TEMP_ROOT
        result, file_name, temp_apk_file = utils.upload_file(apk_file_obj, file_name=file_name_encode,
                                                             cd=TEMP_ROOT, file_suffix=suffix, root_dir="apk")

        if not result:
            message = file_name
            return send_error_response_with_message(message=message)

        # 生成apk包的MD5签名
        with open(temp_apk_file, 'r') as f:
            content = f.read()
            md5.update(content)
            apk_md5 = md5.hexdigest()

        # 在APK包中查找AndroidManifest.xml文件
        import zipfile
        apk_zip_file = zipfile.ZipFile(temp_apk_file, 'r')
        am_bin_file = None
        for fn in apk_zip_file.namelist():
            if fn == 'AndroidManifest.xml':
                am_bin_file = fn
                break
        if not am_bin_file:
            # 如果没找到，认为APK无效
            os.remove(temp_apk_file)
            return send_error_response_with_message(message='apk 无效')
        # 读取APK内容
        temp_am_bin_file_content = apk_zip_file.read(am_bin_file)

        # 将apk包的AndroidManifest.xml存放到临时目录
        am_file_name = 'AndroidManifest%d' % timestamp
        result, file_name, temp_bin_am_file = utils.save_text_to_file(temp_am_bin_file_content, file_name=am_file_name,
                                                                      cd=TEMP_ROOT, file_suffix='xml', root_dir="apk",
                                                                      mode='wb')
        # 通过AXMLPrinter2将APK包中的二进制AndroidManifest.xml转换成可读的内容
        temp_am_xml_file = am_file_name + "_r.xml"
        apk_file_root = os.path.join(TEMP_ROOT, 'apk')
        import subprocess
        p = subprocess.Popen('java -jar %s/AXMLPrinter2.jar %s>%s' % (PRIVATE_ROOT, temp_bin_am_file, temp_am_xml_file),
                             shell=True, stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT, cwd=apk_file_root)
        p.wait()
        # 将二进制文件删除
        os.remove(temp_bin_am_file)
        temp_am_xml_file = os.path.join(apk_file_root, temp_am_xml_file)
        if p.returncode == 1:
            # 转换发错错误了，要将临时文件删除
            os.remove(temp_am_xml_file)
            os.remove(temp_apk_file)
            err_message = p.stdout.read()
            return send_error_response_with_message(message=err_message)
        # 解析AndroidManifest.xml
        # 获取versionCode，versionName，packageName以及flavor
        from xml.dom.minidom import parse
        dom_parser = parse(temp_am_xml_file)
        mainfest_node = dom_parser.documentElement

        def get_attr(node, attr_name):
            if node.hasAttribute(attr_name):
                return node.getAttribute(attr_name)
            attr_name = 'android:%s' % attr_name
            if node.hasAttribute(attr_name):
                return node.getAttribute(attr_name)
        # 获取versionCode
        version_code = get_attr(mainfest_node, 'versionCode')
        # 获取versionName
        version_name = get_attr(mainfest_node, 'versionName')
        # 获取packageName
        package_name = get_attr(mainfest_node, 'package')
        # 获取flavor
        flavor = ''
        application_node = mainfest_node.getElementsByTagName('application')[0]
        meta_data_nodes = application_node.getElementsByTagName('meta-data')
        for meta_data_node in meta_data_nodes:
            if get_attr(meta_data_node, 'name') == 'PRODUCT_FLAVOR':
                flavor = get_attr(meta_data_node, 'value')
                break
        # 将xml删除
        os.remove(temp_am_xml_file)

        from wanbatvcom.settings import CDN_FTP
        dest_path = 'apk/%s/%s/%s' % (package_name, version_name.replace('.', '_'), flavor)
        upload_ok, upload_message = utils.upload_to_ftp(local_name=os.path.basename(temp_apk_file),
                                                        remote_name=apk_file_name,
                                                        src_path=apk_file_root,
                                                        dest_path=dest_path,
                                                        host=CDN_FTP["host"],
                                                        user=CDN_FTP["user"],
                                                        password=CDN_FTP["password"])
        os.remove(temp_apk_file)
        if not upload_ok:
            return send_error_response_with_message(message=upload_message)

        try:
            bd_start_pos = apk_file_name.index('build')
            if bd_start_pos > -1:
                sta = bd_start_pos + len('build')
                end = sta + 8
                build_date = apk_file_name[sta: end]
        except ValueError:
            pass

        if not build_date:
            build_date = datetime.datetime.now().strftime('%y%m%d')

        return send_ok_response_with_data(data={
            "versionCode": version_code,
            "versionName": version_name,
            "packageName": package_name,
            "flavor": flavor,
            "buildType": "release",
            "apkUrl": 'http://%s/%s' % (CDN_URL, upload_message),
            "md5": apk_md5,
            "buildDate": build_date
        })

    def prepublish_res_version(self, request):
        body = request.POST
        app_id = body["app"]

        try:
            application = Application.objects.get(token=app_id)
        except Exception as e:
            return send_error_response_on_exception(e)

        now = datetime.datetime.now()
        timestamp = time.time()
        md5 = hashlib.md5()
        md5.update("t={}&a={}".format(timestamp, app_id))
        version = md5.hexdigest()

        ver_model, _ = AppResVersion.objects.get_or_create(version=version)
        ver_model.publish_date = now
        ver_model.application = application
        ver_model.save()

        return send_ok_response_with_data({
            "version": version,
            "publishDate": now.strftime('%Y-%m-%d %H:%M:%S')
        })

    def save_app_resource_file(self, request, version=None):

        try:
            ver_model = AppResVersion.objects.get(version=version)
        except Exception as e:
            return send_error_response_with_message('无此版本 %s' % version)

        application = ver_model.application

        form_file = request.FILES
        file_object = form_file["file"]
        fn = file_object.name
        suffix = fn.split(".")[1]
        timestamp = time.time()
        md5 = hashlib.md5()
        md5.update("{}{}".format(fn.encode("utf8"), timestamp))
        file_name_encode = md5.hexdigest()
        success, file_name, local_name = utils.upload_file(file_object, file_name=file_name_encode,
                                                           file_suffix='zip', cd=TEMP_ROOT)
        if not success:
            return send_error_response_with_message('保存文件 %s 失败' % fn)

        upload_ok, msg = utils.upload_to_ftp(file_name_encode + '.zip',
                                             src_path=TEMP_ROOT,
                                             dest_path="%s/resources" % application.app_id,
                                             host="upload.sihuacdn.com",
                                             user="wanbatv",
                                             password="wanbatv_upload")
        if not upload_ok:
            return send_error_response_with_message('上传文件失败 %s' % msg)

        try:
            res_model, _ = AppResource.objects.get_or_create(token=file_name_encode)
            res_model.name = file_name_encode + '.zip'
            res_model.version = ver_model
            res_model.data = '/' + msg
            res_model.save()

            return send_ok_response_with_data({
                "token": file_name_encode,
                "name": file_name_encode,
                "url": '/' + msg
            })
        except Exception as e:
            return send_error_response_on_exception(e)

    def save_app_resource_time(self, request, res_token=None):

        try:
            res_model = AppResource.objects.get(token=res_token)
        except Exception as e:
            return send_error_response_on_exception(e)

        body = request.POST
        begin_time_str = body["begintime"]
        end_time_str = body["endtime"]
        res_type = int(body["type"])
        res_model.begin_time = utils.strptime(begin_time_str)
        res_model.end_time = utils.strptime(end_time_str)
        res_model.res_type = res_type
        res_model.save()

        return send_ok_response_with_data({
            "resource": res_token,
            "beginTime": begin_time_str,
            "endTime": end_time_str
        })

    def _sync_res_version(self, request, version, host=None, status=ReleaseStatus.NONE):

        try:
            ver_model = AppResVersion.objects.get(version=version)
        except Exception as e:
            return send_error_response_with_message('无此版本 %s' % version)

        resources = []
        try:
            res_qs = AppResource.objects.filter(version=ver_model)
            for res_model in res_qs:
                resources.append({
                    "token": res_model.token,
                    "name": res_model.name,
                    "beginTime": utils.strftime(res_model.begin_time),
                    "endTime": utils.strftime(res_model.end_time),
                    "data": res_model.data,
                    "type": res_model.res_type
                })

            res_arr_str = json.dumps(resources, encoding='utf8')
            sync_url = 'http://%s/api/common/sync/resversion/%s' % (host, ver_model.application.token)
            sync_data = {
                "version": version,
                "publish": utils.strftime(ver_model.publish_date),
                "resources": res_arr_str
            }
            req = urllib2.Request(sync_url, urllib.urlencode(sync_data))
            res = urllib2.urlopen(req)
            res_content = res.read()
            res.close()
            res_obj = json.loads(res_content, encoding='utf8')
            if res_obj["resultCode"] != 1:
                return send_response_with_result(res_content)

            ver_model.release_status = status
            ver_model.save()

            return send_ok_response_with_data(data={
                "syncResVersion": True
            })

        except Exception as e:
            return send_error_response_on_exception(e)

    def sync_res_version_preview(self, request, version=None):
        return self._sync_res_version(request, version, config.PREVIEW_HOST, ReleaseStatus.PREVIEW)

    def sync_res_version_release(self, request, version=None):
        return self._sync_res_version(request, version, config.RELEASE_HOST, ReleaseStatus.RELEASE)



common = CommonService()
